// Copyright 2008 David Mitchell
// Released under the MIT License

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace NSDuctTape
{
	public static class Application
	{
		/// <summary>
		/// Exposes the given types to the Objective C runtime and
		/// runs the application using the specified nibFile.
		/// </summary>
		/// <param name="nibFile">The name of the nib file.</param>
		/// <param name="exposedTypes">A list of types that should be exposed to the Objective C runtime.</param>
		public static void Run(string nibFile, IEnumerable<Type> exposedTypes)
		{
			IntPtr autoReleasePool = ObjectiveCMethods.CreateInstance(Classes.AutoReleasePool, 0);
			ObjectiveCMethods.SendMessage(autoReleasePool, Selectors.Init);
			try
			{
				IntPtr process = IntPtr.Zero;
				GetCurrentProcess(ref process);
				TransformProcessType(ref process, ProcessType.ForegroundApplication);
				SetFrontProcess(ref process);

				foreach (Type type in exposedTypes)
				{
					ObjectiveCNameAttribute attribute = MemberInfoUtility.GetCustomAttribute<ObjectiveCNameAttribute>(type);
					Registrar.RegisterClass(attribute != null ? attribute.Name : type.Name, type);
				}
				
				IntPtr name = NativeString.StringToNativeString(nibFile);
				IntPtr key = NativeString.StringToNativeString("NSOwner");
				IntPtr sharedApplication = ObjectiveCMethods.SendMessage(Classes.Application, ObjectiveCMethods.SelectorFromString("sharedApplication"));
				IntPtr nsDictionary = ObjectiveCMethods.SendMessage(Classes.Dictionary, ObjectiveCMethods.SelectorFromString("dictionaryWithObject:forKey:"), sharedApplication, key);
				IntPtr zone = ObjectiveCMethods.SendMessage(sharedApplication, ObjectiveCMethods.SelectorFromString("zone"));
				ObjectiveCMethods.SendMessage(Classes.Bundle, ObjectiveCMethods.SelectorFromString("loadNibFile:externalNameTable:withZone:"), name, nsDictionary, zone);
				ObjectiveCMethods.SendMessage(sharedApplication, ObjectiveCMethods.SelectorFromString("run"));
			}
			finally
			{
				ObjectiveCMethods.SendMessage(autoReleasePool, Selectors.Release);
				autoReleasePool = IntPtr.Zero;
			}
		}

		[DllImport("/System/Library/Frameworks/AppKit.framework/AppKit")]
		private static extern void GetCurrentProcess(ref IntPtr processSerialNumber);
		[DllImport("/System/Library/Frameworks/AppKit.framework/AppKit")]
		private static extern void TransformProcessType(ref IntPtr processSerialNumber, ProcessType processType);
		[DllImport("/System/Library/Frameworks/AppKit.framework/AppKit")]
		private static extern void SetFrontProcess(ref IntPtr processSerialNumber);

		private enum ProcessType : uint
		{
			ForegroundApplication = 1,
		}
	}
}
